#include "cqtuart.h"

#include "cprotocoleascii.h"

//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
#include <QThread>

class MyThread : public QThread
{
    public:
        MyThread( CQtUart *i_parent );
        void Stop();
        void run();
    private:
        bool want_stop;
        CQtUart *parent;

};


MyThread::MyThread( CQtUart *i_parent )
{
    this->want_stop = false;
    parent = i_parent;
}

void MyThread::Stop()
{
    this->want_stop = true;
}

void MyThread::run()
{
#ifdef __linux__   /* Linux */
    unsigned char l_read;
#else
    DWORD dwBytesTransferred;
    unsigned char rx_size;
#endif
    unsigned char data;
    unsigned char *rx_msg;

    do
    {
#ifdef __linux__   /* Linux */
        if(parent->PollLinuxComport( &l_read, 1 ) == 1 )
        {

            rx_msg = parent->p_protocole->Decode( l_read );
            if( NULL != rx_msg )
            {
                // on empile le message
                parent->AddMsg( rx_msg );
            }
        }
#else
        ReadFile( parent->hComm, &data, 1U,(LPDWORD) &dwBytesTransferred, &(parent->oRead) ) ;
        GetOverlappedResult( parent->hComm, &(parent->oRead), &dwBytesTransferred, TRUE);
        if( dwBytesTransferred == 1U )
        {
            rx_size = parent->p_protocole->Decode( data, &rx_msg );
            if( NULL != rx_msg )
            {
                // on empile le message
                parent->AddMsg( rx_msg, rx_size );
            }
        }

#endif


    }while( false == this->want_stop );
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
CQtUart::CQtUart(QObject *parent) :
    QObject(parent)
{
#ifdef __linux__   /* Linux */
#else
    hComm = NULL;

#endif
    rx_thread = new MyThread(this);
    this->p_protocole = NULL;
}

//----------------------------------------------------------------------------------------
CQtUart::~CQtUart(void)
{
    this->rx_thread->Stop();
#ifdef __linux__   /* Linux */
#else
    if( hComm != NULL )
    {
        Close();
    }
#endif

    if( NULL != this->rx_thread )
    {
        delete this->rx_thread;
    }

    if( NULL != this->p_protocole )
    {
        delete this->p_protocole;
    }
}

//----------------------------------------------------------------------------------------
bool CQtUart::Open( char *ComPort, unsigned long BaudRate, unsigned char DataSize,
                    unsigned char Parity, unsigned char NbStopBit, EProtocol i_protocole
                  )
{
        bool RValue;
#ifdef __linux__   /* Linux */
        RValue = true;
          int baudr;

          switch(BaudRate)
          {
            case      50 : baudr = B50;
                           break;
            case      75 : baudr = B75;
                           break;
            case     110 : baudr = B110;
                           break;
            case     134 : baudr = B134;
                           break;
            case     150 : baudr = B150;
                           break;
            case     200 : baudr = B200;
                           break;
            case     300 : baudr = B300;
                           break;
            case     600 : baudr = B600;
                           break;
            case    1200 : baudr = B1200;
                           break;
            case    1800 : baudr = B1800;
                           break;
            case    2400 : baudr = B2400;
                           break;
            case    4800 : baudr = B4800;
                           break;
            case    9600 : baudr = B9600;
                           break;
            case   19200 : baudr = B19200;
                           break;
            case   38400 : baudr = B38400;
                           break;
            case   57600 : baudr = B57600;
                           break;
            case  115200 : baudr = B115200;
                           break;
            case  230400 : baudr = B230400;
                           break;
            case  460800 : baudr = B460800;
                           break;
            case  500000 : baudr = B500000;
                           break;
            case  576000 : baudr = B576000;
                           break;
            case  921600 : baudr = B921600;
                           break;
            case 1000000 : baudr = B1000000;
                           break;
            default      :  // to do
                           break;
          }
          //printf("com :%s", comports[comport_number]);

          this->Cport = open(ComPort, O_RDWR | O_NOCTTY | O_NDELAY);
          if(this->Cport==-1)
          {
              RValue = false;
            //perror("unable to open comport ");    // to do
          }

          error = tcgetattr(this->Cport, &(this->old_port_settings));
          if(error==-1)
          {
            close(this->Cport);
            RValue = false;
            //perror("unable to read portsettings ");
          }
          memset(&new_port_settings, 0, sizeof(new_port_settings));  /* clear the new struct */

          new_port_settings.c_cflag = baudr | CS8 | CLOCAL | CREAD;
          new_port_settings.c_iflag = IGNPAR;
          new_port_settings.c_oflag = 0;
          new_port_settings.c_lflag = 0;
          new_port_settings.c_cc[VMIN] = 0;      /* block untill n bytes are received */
          new_port_settings.c_cc[VTIME] = 0;     /* block untill a timer expires (n * 100 mSec.) */
          error = tcsetattr(this->Cport, TCSANOW, &new_port_settings);
          if(error==-1)
          {
            close(this->Cport);
            RValue = false;
            //perror("unable to adjust portsettings ");
            //return(1);
          }

#else
          RValue = false;
        if( hComm == NULL )
        {
                hComm = CreateFileA( ComPort,
                                     GENERIC_READ | GENERIC_WRITE,
                                     FILE_SHARE_WRITE | FILE_SHARE_READ,	// must be opened with exclusive-access
                                     NULL,					// no security attributes
                                     OPEN_EXISTING,				// must use OPEN_EXISTING
                                     FILE_FLAG_OVERLAPPED,			// not overlapped I/O
                                     NULL					// hTemplate must be NULL for comm devices
                                    );


                if(hComm != INVALID_HANDLE_VALUE)
                {
                        BOOL fSuccess;

                        m_dcb.DCBlength = sizeof (DCB);

                        GetCommState(hComm, &m_dcb);

                        // Change the DCB structure settings.
                        m_dcb.BaudRate = BaudRate;              // Current baud
                        m_dcb.fBinary = TRUE;               // Binary mode; no EOF check
                        m_dcb.fParity = FALSE;               // Enable parity checking
                        m_dcb.fOutxCtsFlow = FALSE;         // No CTS output flow control
                        m_dcb.fOutxDsrFlow = FALSE;         // No DSR output flow control
                        m_dcb.fDtrControl = DTR_CONTROL_DISABLE;
                                                              // DTR flow control type
                        m_dcb.fDsrSensitivity = FALSE;      // DSR sensitivity
                        m_dcb.fTXContinueOnXoff = TRUE;     // XOFF continues Tx
                        m_dcb.fOutX = FALSE;                // No XON/XOFF out flow control
                        m_dcb.fInX = FALSE;                 // No XON/XOFF in flow control
                        m_dcb.fErrorChar = FALSE;           // Disable error replacement
                        m_dcb.fNull = FALSE;                // Disable null stripping
                        m_dcb.fRtsControl = RTS_CONTROL_DISABLE;
                                                              // RTS flow control
                        m_dcb.fAbortOnError = FALSE;        // Do not abort reads/writes on
                                                              // error
                        m_dcb.ByteSize = DataSize;                 // Number of bits/byte, 4-8
                        m_dcb.Parity = Parity;            // 0-4=no,odd,even,mark,space
                        m_dcb.StopBits = NbStopBit;        // 0,1,2 = 1, 1.5, 2


                        fSuccess = SetCommState(hComm,&m_dcb);

                        FlushFileBuffers( hComm ) ;

                        if( fSuccess)
                        {
                            fSuccess = SetCommMask (hComm, EV_RXCHAR | EV_BREAK );

                        }

                        if( fSuccess)
                        {
                                RValue = true;
                                // Create an event object for use by WaitCommEvent.

                                oRead.hEvent = CreateEvent(
                                        NULL,   // default security attributes
                                        FALSE,  //  reset event
                                        FALSE,  // not signaled
                                        NULL    // no name
                                        );

                                oWrite.hEvent = CreateEvent(
                                        NULL,   // default security attributes
                                        FALSE,  // auto reset event
                                        FALSE,  // not signaled
                                        NULL    // no name
                                        );

                                // Intialize the rest of the OVERLAPPED structure to zero.
                                oRead.Internal = 0;
                                oRead.InternalHigh = 0;
                                oRead.Offset = 0;
                                oRead.OffsetHigh = 0;

                                // Intialize the rest of the OVERLAPPED structure to zero.
                                oWrite.Internal = 0;
                                oWrite.InternalHigh = 0;
                                oWrite.Offset = 0;
                                oWrite.OffsetHigh = 0;

                                // initialise le protocole
                        }
                        else
                        {
                            CloseHandle( hComm );
                            hComm = NULL;
                        }
                }
                else
                {
                    hComm = NULL;
                }
        }
#endif
        if (RValue == true)
        {
            switch( i_protocole )
            {
            case E_PROTOCOL_ASCII:
                {
                    this->p_protocole = new CProtocoleAscii();
                    if( false == this->rx_thread->isRunning() )
                    {
                        this->rx_thread->start();
                    }
                }
                break;

            default:
                {
                    if( NULL != this->p_protocole )
                    {
                        delete this->p_protocole;
                    }

                    this->p_protocole = NULL;
                }
                break;
            }
        }

        return RValue;
}

//----------------------------------------------------------------------------------------
bool CQtUart::Close( void )
{
        bool ret;
        ret = true;

        this->rx_thread->Stop();
#ifdef __linux__   /* Linux */
        close(this->Cport);
        tcsetattr(this->Cport, TCSANOW, &old_port_settings);
#else

        CloseHandle( hComm );



        hComm = NULL;
#endif

        return ret;
}

//----------------------------------------------------------------------------------------
bool CQtUart::Read( unsigned char *o_pBuffer, unsigned char *o_size )
{
    bool o_success = false;

    if( false == this->rx_msg.isEmpty() )
    {
        unsigned char loop;
        QList<unsigned char> toto = this->rx_msg.dequeue();

        *o_size = toto.size();
        for( loop=0; loop<*o_size; loop++ )
        {
            out_rx_msg[loop] = toto.value(loop);
        }

        o_success = true;
    }

    return o_success;
}

//----------------------------------------------------------------------------------------
bool CQtUart::Write( unsigned char *i_pBuffer, unsigned char i_size)
{
    bool o_success = false;
    unsigned char *o_msg;

    if( NULL != this->p_protocole )
    {
        o_msg = this->p_protocole->EncodeMsg( i_pBuffer, &i_size );

        if( NULL != o_msg )
        {
#ifdef __linux__   /* Linux */
            o_success = write(this->Cport, o_msg, i_size);
#else
            DWORD written_bytes ;

            WriteFile(hComm,o_msg, i_size, &written_bytes, &oWrite ) ;

            GetOverlappedResult(hComm, &oWrite, &written_bytes, TRUE);

            o_success = true;
#endif
        }
    }

    return o_success;
}

//----------------------------------------------------------------------------------------
void CQtUart::AddMsg( unsigned char *i_buffer, unsigned char i_size )
{
    // copy le message dans une fifo
    QList<unsigned char> toto;
    unsigned char loop;

    for( loop=0U; loop<i_size; loop++ )
    {
        toto.append(i_buffer[loop]);
    }

    this->rx_msg.enqueue(toto);

    emit SignalAddMsg();
}

//----------------------------------------------------------------------------------------
QList<unsigned char> CQtUart::GetMsg( void )
{
    QList<unsigned char> o_list;

    if( false == this->rx_msg.isEmpty() )
    {
        o_list = this->rx_msg.dequeue();
    }

    return o_list;
}
int CQtUart::PollLinuxComport(unsigned char *buf, int size)
{
          int n = 0;
#ifdef __linux__   /* Linux */


    #ifndef __STRICT_ANSI__                       /* __STRICT_ANSI__ is defined when the -ansi option is used for gcc */
    //  if(size>SSIZE_MAX)  size = (int)SSIZE_MAX;  /* SSIZE_MAX is defined in limits.h */
    #else
      if(size>4096)  size = 4096;
    #endif


  n = read(this->Cport, buf, size);
#endif
  return(n);

}
